package top.dingwen.io.treasure.websocket.core.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import top.dingwen.io.treasure.base.constant.SupWarsConstant;
import top.dingwen.io.treasure.base.util.str.StringPlusUtils;
import top.dingwen.io.treasure.websocket.autoconfigure.WebSocketProperties;
import top.dingwen.io.treasure.websocket.constant.WebSocketConstant;
import top.dingwen.io.treasure.websocket.core.call.IReceiveMsgCall;
import top.dingwen.io.treasure.websocket.core.holder.WebSocketSessionHolder;
import top.dingwen.io.treasure.websocket.core.WebSocketMsg;
import top.dingwen.io.treasure.websocket.util.WebSocketUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;


/**
 * WebSocketHandler 实现类
 *
 * @author dingwen
 * @since 2024/10/16
 */
@Slf4j
@Component
@SuppressWarnings(SupWarsConstant.ALL)
public class WebSocketHandler extends AbstractWebSocketHandler {


    @Resource
    private WebSocketProperties websocketProperties;

    @Resource
    private IReceiveMsgCall receiveMsgCall;

    /**
     * 连接成功后
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws IOException {
        Map<String, Object> attributes = session.getAttributes();
        String loginId = Convert.toStr(attributes.get(WebSocketConstant.LOGIN_ID_KEY));
        String loginName = Convert.toStr(attributes.get(WebSocketConstant.LOGIN_NAME_KEY));
        if (Convert.toBool(websocketProperties.getAuth(), true) && ObjectUtil.isNull(loginId)) {
            session.close(CloseStatus.BAD_DATA);
            log.info("{},{},您没有权限操作,请先完成认证,sessionId:[{}],loginId:[{}],loginName:[{}]",
                    WebSocketConstant.WS_L_O_S, WebSocketConstant.WS_L_H,
                    loginId, session.getId(),
                    loginName
            );
            return;
        }
        WebSocketSessionHolder.addSession(loginId, session);
        log.info("{},{},有新的连接 sessionId:[{}],loginId:[{}],userName:[{}]",
                WebSocketConstant.WS_L_O_S, WebSocketConstant.WS_L_H,
                session.getId(), loginId, loginName);
    }

    /**
     * 处理接收到的文本消息
     *
     * @param session WebSocket会话
     * @param message 接收到的文本消息
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        // 从WebSocket会话中获取登录用户信息
        Map<String, Object> attributes = session.getAttributes();
        String loginId = Convert.toStr(attributes.get(WebSocketConstant.LOGIN_ID_KEY));
        String sendLoginId = Convert.toStr(attributes.get(WebSocketConstant.SEND_LOGIN_ID_KEY));
        String payload = message.getPayload();
        if (WebSocketConstant.PING.equals(payload)) {
            WebSocketUtils.sendPongMessage(session);
            WebSocketSessionHolder.updateActiveTime(loginId);
            log.info("{},{},客户端心跳检测,客户端发送ping已回复ping,将进行保活,loginId:{}", WebSocketConstant.WS_L_O_S,
                    WebSocketConstant.WS_L_HB_CJ, loginId);
            return;
        }
        String textMsg = message.getPayload();
        List<String> sessionKeys = StringPlusUtils.str2List(sendLoginId);
        if (CollUtil.isNotEmpty(sessionKeys)) {
            // 创建WebSocket消息
            WebSocketMsg webSocketMsg =
                    WebSocketMsg
                            .builder()
                            .sessionKeys(StringPlusUtils.str2List(sendLoginId))
                            .message(textMsg)
                            .build();
            WebSocketUtils.publishMessage(webSocketMsg);
        }
        // 执行回调
        sessionKeys.stream().forEach(sessionKey
                -> receiveMsgCall.receiveTextMsgCall(Convert.toLong(loginId), Convert.toLong(sendLoginId), textMsg)
        );
    }

    /**
     * 处理接收到的二进制消息
     *
     * @param session WebSocket会话
     * @param message 接收到的二进制消息
     * @throws Exception 处理消息过程中可能抛出的异常
     */
    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        String loginId = Convert.toStr(attributes.get(WebSocketConstant.LOGIN_ID_KEY));
        String sendLoginId = Convert.toStr(attributes.get(WebSocketConstant.SEND_LOGIN_ID_KEY));
        super.handleBinaryMessage(session, message);
        // 执行回调
        receiveMsgCall.receiveBinaryMsgCall(Convert.toLong(loginId), Convert.toLong(sendLoginId), message.getPayload().array());
    }

    /**
     * 处理接收到的Pong消息（心跳监测）
     *
     * @param session WebSocket会话
     * @param message 接收到的Pong消息
     */
    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) {
        WebSocketUtils.sendPongMessage(session);
        String sessionKey = Convert.toStr(session.getAttributes().get(WebSocketConstant.LOGIN_ID_KEY));
        WebSocketSessionHolder.updateActiveTime(sessionKey);
        log.info("{},{},客户端心跳检测,客户端回复Pong已进行保活,loginId:{}", WebSocketConstant.WS_L_O_S,
                WebSocketConstant.WS_L_HB_CJ, sessionKey);
    }

    /**
     * 处理WebSocket传输错误
     *
     * @param session   WebSocket会话
     * @param exception 发生的异常
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        String sessionKey = Convert.toStr(session.getAttributes().get(WebSocketConstant.LOGIN_ID_KEY));
        log.error("{},{},传输错误, sessionId:[{}],消息:[{}],sessionKey:[{}]",
                WebSocketConstant.WS_L_O_S, WebSocketConstant.WS_L_H,
                session.getId(), exception.getMessage(), sessionKey, exception);
        WebSocketSessionHolder.removeSession(sessionKey);
    }

    /**
     * 在WebSocket连接关闭后执行清理操作
     *
     * @param session WebSocket会话
     * @param status  关闭状态信息
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        Map<String, Object> attributes = session.getAttributes();
        String loginId = Convert.toStr(attributes.get(WebSocketConstant.LOGIN_ID_KEY));
        String loginName = Convert.toStr(attributes.get(WebSocketConstant.LOGIN_NAME_KEY));
        if (ObjectUtil.isNull(loginId)) {
            log.info("{},{},已断开连接,sessionId:[{}],loginId:[{}],loginName:[{}]",
                    WebSocketConstant.WS_L_O_S, WebSocketConstant.WS_L_H,
                    loginId, session.getId(),
                    loginName
            );
            return;
        }
        WebSocketSessionHolder.removeSession(loginId);
        log.info("{},{},断开连接, sessionId:[{}],loginId:[{}],loginName:[{}]",
                WebSocketConstant.WS_L_O_S, WebSocketConstant.WS_L_H,
                session.getId(), loginId, loginName);
    }

    /**
     * 指示处理程序是否支持接收部分消息
     *
     * @return 如果支持接收部分消息，则返回true；否则返回false
     */
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

}
